package org.thss.petri.gef.editparts.common;

import org.eclipse.draw2d.ConnectionAnchor;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.gef.ConnectionEditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.NodeEditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.editparts.AbstractGraphicalEditPart;
import org.thss.petri.gef.editparts.policies.NodeComponentEditPolicy;
import org.thss.petri.gef.editparts.policies.NodeGraphicalNodeEditPolicy;
import org.thss.petri.gef.figures.NodeFigure;
import org.thss.petri.model.Node;
import org.thss.petri.model.NodeConstraint;
import org.thss.petri.model.PetriPackage;

public abstract class PetriNetNodeEditPart extends AbstractGraphicalEditPart
		implements Adapter, NodeEditPart {
	
	protected Node model;
	
	public PetriNetNodeEditPart(Node model) {
		this.model = model;
		setModel(model);
	}

	@Override
	protected abstract IFigure createFigure();

	@Override
	protected void createEditPolicies() {
		installEditPolicy(EditPolicy.COMPONENT_ROLE, new NodeComponentEditPolicy());
		installEditPolicy(EditPolicy.GRAPHICAL_NODE_ROLE, new NodeGraphicalNodeEditPolicy());
	}
	
	@Override
	protected void refreshVisuals() {
		Node node = (Node) getModel();
		NodeConstraint constraint = node.getConstraint();
		Point location = new Point(constraint.getX(), constraint.getY());
		Dimension size = new Dimension(constraint.getWidth(), constraint.getHeight());
		Rectangle rectangle = new Rectangle(location, size);
		NodeFigure figure = (NodeFigure) getFigure();
		figure.setName(node.getName());
		((GraphicalEditPart) getParent()).setLayoutConstraint(this, figure, rectangle);
	}
	
	protected abstract ConnectionAnchor getNodeAnchor();
	
	@Override
	public ConnectionAnchor getSourceConnectionAnchor(
			ConnectionEditPart connection) {
		return getNodeAnchor();
	}

	@Override
	public ConnectionAnchor getTargetConnectionAnchor(
			ConnectionEditPart connection) {
		return getNodeAnchor();
	}

	@Override
	public ConnectionAnchor getSourceConnectionAnchor(Request request) {
		return getNodeAnchor();
	}

	@Override
	public ConnectionAnchor getTargetConnectionAnchor(Request request) {
		return getNodeAnchor();
	}

	@Override
	public void activate() {
		if (isActive()) {
			return;
		}
		super.activate();
		((Notifier) getModel()).eAdapters().add(this);
	}
	
	@Override
	public void deactivate() {
		if (!isActive()) {
			return;
		}
		super.deactivate();
		((Notifier) getModel()).eAdapters().remove(this);
	}
	
	@Override
	public void notifyChanged(Notification notification) {
		int id = notification.getFeatureID(PetriPackage.class);
		switch (id) {
		case PetriPackage.NODE__CONSTRAINT:
			refreshChildren();
			refreshVisuals();
			break;
		case PetriPackage.NODE__NAME:
			refreshVisuals();
			break;
		case PetriPackage.NODE__OUTGOINGS:
			refreshSourceConnections();
			break;
		case PetriPackage.NODE__INCOMINGS:
			refreshTargetConnections();
			break;
		}
	}

	@Override
	public Notifier getTarget() {
		return model;
	}

	@Override
	public void setTarget(Notifier newTarget) {
		if (newTarget == null) {
			return;
		}
		model = (Node) newTarget;
		setModel(model);
	}

	@Override
	public boolean isAdapterForType(Object type) {
		return type.equals(getModel().getClass());
	}
	
}
